
#ifndef LOD_DISP_H
#define LOD_DISP_H


//#include "../light/lighting.h"
//#include "../camera.h"

#include <map>
#include <vector>
#include <string>

#include <assert.h>
#include <stdlib.h>
//#include <GL/gl.h>
//#include <GL/glu.h>
#include <GL/glew.h>
#include <GL/glut.h>

#include "../types.h"
#include "../gl_function_wrappers.h"

// todo
#include <iostream>
using std::cout;
using std::endl;


class disp
{
    public:
    virtual void display(int width, int height) = 0;

    virtual disp* keyboard(unsigned char key, int x, int y)
    {
        return this;
    }

    virtual void keyboard_special(int key, int x, int y)
    {
    }

    virtual void reshape(int width, int height)
    {
    }
    
    virtual void mouse(int button, int state, int x, int y)
    {
    }

    virtual void motion(int x, int y)
    {
    }

    virtual void passive_motion(int x, int y)
    {
    }

    //! called on a screen when it becomes active
    virtual void init()
    {
        lmb_state = GLUT_UP;
        rmb_state = GLUT_UP;
    }

    // called on a screen that is going to be deactivated 
    virtual void deinit()
    {
    }

    virtual void visibility(int vis)
    {
        //if(vis!=GLUT_VISIBLE)
        //    glutIdleFunc(NULL);
    }

    void set_child(short pos, disp* child)
    {
        children[pos] = child;
    }

    void set_parent(disp* par)
    {
        parent = par;
    }


    disp(void* fnt, const comp_type* menu_color4, const comp_type* act_menu_color4, 
         const char* Text[], const int* Yinc_text, const int* Xoffset_text,
         const bool* Show_text, const bool* Menu_pos, int text_count, 
         const char* helpstr[]=NULL, int helpcount=0)
     : font(fnt),
       act_menu_pos(0),  // de
       show_help(false),  //  fau
       lmb_state(GLUT_UP), //    lt
       rmb_state(GLUT_UP)  //      s
    {
        menu_color[0] = menu_color4[0];
        menu_color[1] = menu_color4[1];
        menu_color[2] = menu_color4[2];
        menu_color[3] = menu_color4[3];

        act_menu_color[0] = act_menu_color4[0];
        act_menu_color[1] = act_menu_color4[1];
        act_menu_color[2] = act_menu_color4[2];
        act_menu_color[3] = act_menu_color4[3];

        for(int i=0; i<text_count; i++) {
            text.push_back(Text[i]);
            yinc_text.push_back(Yinc_text[i]);
            xoffset_text.push_back(Xoffset_text[i]);
            show_text.push_back(Show_text[i]);
            menu_pos.push_back(Menu_pos[i]);
        }

        for(int i=0; i<helpcount; i++) 
            help_str.push_back(helpstr[i]);
    }

    virtual ~disp()
    {
    }


    //*****    PROTECTED  **************************************

    protected:
    void push_ortho(int width, int height) 
    {
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glLoadIdentity();
        gluOrtho2D(0,width,0,height);

        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity();
    }

    void pop_ortho() 
    {
        glMatrixMode(GL_PROJECTION);
        glPopMatrix();
        glMatrixMode(GL_MODELVIEW);
        glPopMatrix();
    }


    // my idea of drawing strings on the screen is like this:
    // we draw in a rectangular field starting in a set point (xstart_text,ystart_text);
    // what is displayed are strings (vector text), one after another, but only those 
    // marked to be drawn (in show_text vector); each string is drawn at specified
    // x offset (vector xoffset_text [+xstart_text, for every string]), couple of pixels
    // below the previous string (yinc_text ~= y increment)

    // a notice: turned on lighting will cause (most probably) unwanted results
    void draw_string(const char* str, float x, float y) 
    {
        //
        //dlighting.disable();
        glRasterPos2f(x, y);
        int l = strlen(str);
        int i = 0;
        while(i < l)
            glutBitmapCharacter(font, str[i++]);
    }

    // you have to take care about the lighting (to turn off the lighting)
    void quick_draw_string(int width,int height,const char* str,float x,float y)
    {
        glDisable(GL_DEPTH_TEST);
        push_ortho(width,height);

        set_menu_color();
        draw_string(str,x,y);

        pop_ortho();
        glEnable(GL_DEPTH_TEST);
    }
                                                //indices of emphasised labels
    void disp_text(int width, int height, std::vector<int> emph, 
            const comp_type* col4)
    {
        int end;
        int yact;

        push_ortho(width, height);
        glDisable(GL_DEPTH_TEST);
        set_menu_color();

        if(emph.size()>0) {
            end = show_text.size();
            yact = ystart_text;
            for(int i=0; i<end; i++) 
            {
                if(show_text[i]) {
                    yact += yinc_text[i];
                    bool ch=false;

                    if(vec_contains(emph, i)) { // emphasise this label
                        if(i==act_menu_pos)
                            // if the focus is under emphasised position, mix the colors
                            GLCOLOR4((col4[0] + act_menu_color[0])/2,
                                     (col4[1] + act_menu_color[1])/2,
                                     (col4[2] + act_menu_color[2])/2,
                                     (col4[3] + act_menu_color[3])/2);
                        else
                            GLCOLOR4V(col4);
                        ch=true;
                    }
                    else if(i == act_menu_pos) { // present focus position
                        set_act_menu_color();
                        ch=true;
                    }
                    draw_string(text[i].c_str(), xstart_text + xoffset_text[i], yact);
                    if(ch)
                        set_menu_color();
                }
            }
        }
        else {
            end = show_text.size();
            yact = ystart_text;
            for(int i=0; i<end; i++) {
                // xstart_text, yinc_text, xoffset_text, show_text
                if(show_text[i]) {
                    yact += yinc_text[i];
                    if(i == act_menu_pos) // change the color just for a moment
                        set_act_menu_color();
                    draw_string(text[i].c_str(), xstart_text + xoffset_text[i], yact);
                    if(i == act_menu_pos)
                        set_menu_color();
                }
            }
        }

        glEnable(GL_DEPTH_TEST);
        pop_ortho();
    }

    void disp_text(int width, int height)
    {
        disp_text(width,height, std::vector<int>(), NULL);
    }

    void disp_help(int width, int height)
    {
        float xstart=1.0f/3 * width;
        float ystart = height - 1.0f/4 * height;
        int move=30;

        glDisable(GL_DEPTH_TEST);
        push_ortho(width,height);

        set_menu_color();
        unsigned end=help_str.size();
        for(unsigned i=0; i<end; i++)
            draw_string(help_str[i].c_str(),xstart,ystart-i*move);

        pop_ortho();
        glEnable(GL_DEPTH_TEST);
    }

    bool vec_contains(std::vector<int> vec, int val)
    {
        // good for small vectors (should be faster than if branching)
        int ret=0;
        for(unsigned i=0; i<vec.size(); i++)
            ret += (int)(vec[i]==val);
        return ret>0;
    }

    disp* change_menu(bool way)
    {
        if(way) // oh yes, go deeper
            return children[act_menu_pos];
        else // or go back
            return parent;
    }

    
    

    void act_menu_pos_up() // (but the number of the position goes down)
    {
        short i = act_menu_pos;
        bool done = false;
        while(i-- > 0)
            //if(show_text[i]) {
            if(menu_pos[i]) {
                done = true;
                break;
            }
        if(!done) {
            //i = show_text.size();
            i = menu_pos.size();
            while(i-- > 0)
                //if(show_text[i]) {
                if(menu_pos[i]) {
                done = true;
                break;
            }
        }
        assert(done==true);
        act_menu_pos = i;
    }

    void act_menu_pos_down() // (but the number of the position goes up)
    {
        short i = act_menu_pos;
        bool done = false;
        //while(i++ < short(show_text.size()-1))
        while(i++ < short(menu_pos.size()-1))
           //if(show_text[i]) {
           if(menu_pos[i]) {
               done = true;
               break;
           }
        if(!done) {
            i = -1;
            //while(i++ < short(show_text.size()-1)) {
            while(i++ < short(menu_pos.size()-1)) {
                //if(show_text[i]) {
                if(menu_pos[i]) {
                    done = true;
                    break;
                }
            }
        }
        assert(done==true);
        act_menu_pos = i;
    }

    //void swap_help_display()
    //{
    //    show_help = !show_help;
    //}


    void set_menu_color()
    {
        GLCOLOR4V(menu_color);
    }

    void set_act_menu_color()
    {
        GLCOLOR4V(act_menu_color);
    }

    void set_act_menu_pos(short pos)
    {
        act_menu_pos = pos;
    }

    short get_act_menu_pos()
    {
        return act_menu_pos;
    }

    //void set_show_help(bool show)
    //{
    //    show_help = show;
    //}

    //bool get_show_help()
    //{
    //    return show_help;
    //}

    void set_xstart_text(int xstart)
    {
        xstart_text = xstart;
    }

    int get_xstart_text()
    {
        return xstart_text;
    }

    void set_ystart_text(int ystart)
    {
        ystart_text = ystart;
    }

    int get_ystart_text()
    {
        return ystart_text;
    }

    void change_text(int pos, const char* new_text)
    {
        text[pos] = std::string(new_text);
    }

    void menu_pos_visible(int pos, bool vis)
    {
        show_text[pos] = vis;
    }

    void menu_pos_visible(int start_pos, int end_pos, bool vis)
    {
        for(; start_pos<=end_pos; start_pos++)
            show_text[start_pos] = vis;
    }

    void menu_pos_active(int pos, bool a)
    {
        menu_pos[pos] = a;
    }
    


    private:
    disp* parent;
    std::map<short,disp*> children;

    void* font;
    comp_type menu_color[4];
    comp_type act_menu_color[4];

    std::vector<std::string> text;
    std::vector<int> xoffset_text;
    std::vector<int> yinc_text;
    std::vector<bool> show_text;
    std::vector<bool> menu_pos;
    int xstart_text;
    int ystart_text;

    std::vector<std::string> help_str;

    short act_menu_pos;
    //bool show_help;

    protected:
    bool show_help;
    bool lmb_state;
    bool rmb_state;
    //int click_x;
    //int click_y;
};


#endif // LOD_DISP_H
